﻿using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using WebAPI_Demo.Models;
using WebAPI_Demo.Models.Database;

namespace WebAPI_Demo.Controllers
{
    /// <summary>
    /// 缓存控制器
    /// </summary>
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class CacheController : ControllerBase
    {
        private readonly IMemoryCache cache;
        private readonly Store_2022Context _db;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="cache"></param>
        public CacheController(IMemoryCache cache, Store_2022Context db)
        {
            this.cache = cache;
            _db = db;
        }

        /// <summary>
        /// 接口：返回当前的时间
        /// </summary>
        /// <returns></returns>
        [ResponseCache(Duration = 20)]
        [HttpGet]
        public DateTime Now()
        {
            return DateTime.Now;
        }

        /// <summary>
        /// 判断缓存是否存在
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public ActionResult<string> TryGetValueCache()
        {
            //尝试获取一个值
            //cache.TryGetValue("key",参数) 如果有值 返回true 否则false
            //获取有值得情况下 获取那个值。
            //一个函数 返回两个结果 out 关键字      out _ 指放弃这个变量 
            object obj; //基类
            if (cache.TryGetValue("UserName", out obj))
            {
                //obj就是获取回来的值
                return "该缓存存在";
            }
            else
            {
                return "该缓存不存在";
            }
        }

        /// <summary>
        /// 读写缓存 [写Set 读Get]
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public ActionResult<string> GetCache()
        {
            // 写入缓存
            // cache.Set("key","value")
            // cache.Set("e6","900")
            cache.Set("UserName", "admin");
            cache.Set("Password", "12345");

            // 读取缓存
            // 读取e6
            // cache.Get<接收保存的数据类型>("key");
            string userName = cache.Get<string>("UserName");
            string password = cache.Get<string>("Password");

            // 返回
            return userName + "\n" + password;
        }

        //out ref
        //值传递 引用传递   C/JS
        [HttpGet]
        public int A()
        {
            //定义变量x=1 在A的执行域开辟空间 x 赋值为 1
            int x = 1;
            //调用B方法 传递 x的 1 进入B方法
            //2、传递的是x的指针  内存的地址
            B(out x);
            return x; //x==??? 1  /  9
        }

        [NonAction]
        public void B(out int x)//在B的执行域开辟一个内存空间 x  同时1赋值给x
        {
            //修改 
            x = 9;
        }

        [HttpGet]
        public Good C()
        {
            var good = new Good
            {
                Name = "小米手机"
            };
            D(good);
            return good;
        }

        /// <summary>
        /// 当传递的参数类型 是对象的时候 默认就是引用传递 传递的是对象的地址（指针）
        /// </summary>
        /// <param name="good"></param>
        [NonAction]
        public void D(Good good)
        {
            good = new Good
            {
                //对象的Name属性赋值为
                Name = "华为手机"
            };
        }

        /// <summary>
        /// 读写缓存 [GetOrCreate可以实现：如果一个缓存值存在，则返回它，如果不存在，则创建该缓存后再返回它。]
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public ActionResult<string> GetOrCreateCache()
        {
            //如果一个缓存值存在，则返回它，如果不存在，则创建该缓存后再返回它（给一个默认的值返回）。
            // cache.GetOrCreate("UserName",表达式);表达式 委托（把函数（匿名函数）当参数传递）
            //获取key为UserName的缓存值 如果没有 则返回默认值"admin"
            string userName = cache.GetOrCreate("UserName", entry =>
            {
                //写一些逻辑去计算

                //返回一个字符串
                return "admin";
            });
            string password = cache.GetOrCreate("Password", entry =>
            {
                return "12345";
            });
            return userName + "\n" + password;
        }

        /// <summary>
        /// 删除缓存
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public ActionResult<string> RemoveCache()
        {
            // 写入缓存
            cache.Set("UserName", "admin");
            cache.Set("Password", "12345");

            // 删除缓存     key
            cache.Remove("UserName");
            cache.Remove("Password");

            // 返回
            return "缓存删除成功";
        }

        /// <summary>
        /// 永不过期
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public ActionResult<string> CacheTime1()
        {
            string msg = string.Empty;
            cache.Set("UserName", "admin");

            // 读取缓存
            for (int i = 1; i <= 5; i++)
            {
                msg += $"第{i}秒缓存值：{cache.Get<string>("UserName")}   ";
                Thread.Sleep(1000);
            }

            // 返回
            return msg;
        }

        /// <summary>
        /// 绝对时间过期
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public ActionResult<string> CacheTime2()
        {
            string msg = string.Empty;
            //过期策略 3秒后 多少时间后必定过期 
            //TimeSpan.FromSeconds(3)时间间隔
            cache.Set("UserName", "admin", TimeSpan.FromSeconds(3));

            // 读取缓存 1 - 5
            for (int i = 1; i <= 5; i++)
            {
                //拼接字符串 获取缓存的值
                msg += $"第{i}秒缓存值：{cache.Get<string>("UserName")}\n";
                //线程睡眠 暂停代码执行1秒（1000毫秒）
                Thread.Sleep(1000);
            }

            // 返回
            return msg;
        }

        /// <summary>
        /// 滑动时间过期
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public ActionResult<string> CacheTime3()
        {
            string msg = string.Empty;
            cache.Set("UserName", "admin", new MemoryCacheEntryOptions
            {
                //滑动过期时间设置
                SlidingExpiration = TimeSpan.FromSeconds(3)
            });

            // 读取缓存
            for (int i = 1; i <= 5; i++)
            {
                msg += $"第{i}秒缓存值：{cache.Get<string>("UserName")}\n";
                Thread.Sleep(1000);
            }

            // 返回
            return msg;
        }

        /// <summary>
        /// 绝对时间过期+滑动时间过期
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public ActionResult<string> Get()
        {
            string msg = string.Empty;
            cache.Set("UserName", "admin", new MemoryCacheEntryOptions
            {
                //两个策略 滑动事件1.5S内有访问延续时间
                SlidingExpiration = TimeSpan.FromSeconds(1.5),
                //绝对时间3秒 3秒后必定过期
                AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(3)
                //只要有一个策略被执行 另外一个也会失效
            });

            // 读取缓存
            for (int i = 1; i <= 5; i++)
            {
                msg += $"第{i}秒缓存值：{cache.Get<string>("UserName")}\n";
                Thread.Sleep(1000);
            }

            // 返回
            return msg;
        }

        /// <summary>
        /// 获取商品列表数据，本地缓存保存查询结果，缓存时间为30分钟
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public List<Good> GoodListCache30m(int id, string KeyWord)
        {
            object obj; //基类
            if (cache.TryGetValue("GoodList_" + id + "_" + KeyWord, out obj))
            {
                //obj就是获取回来的值
                //return "该缓存存在";
                return obj as List<Good>;
            }
            else
            {
                //获取商品操作
                var goodList = _db.Goods.AsQueryable();
                //如果查询的商品Id>0 
                if (id > 0)
                {
                    goodList = goodList.Where(x => x.Id == id);
                }
                //如果查询的关键字不为空
                if (!string.IsNullOrEmpty(KeyWord))
                {
                    goodList = goodList.Where(x => x.Name.Contains(KeyWord));
                }
                cache.Set("GoodList_" + id + "_" + KeyWord, goodList.ToList(), TimeSpan.FromMinutes(30));
                return goodList.ToList();
            }


        }

        /// <summary>
        /// 浏览记录
        /// </summary>
        /// <param name="id"></param>
        [HttpGet]
        public string CacheBrowse(string id)
        {



            if (cache.Get<List<string>>("ShopBrowse") == null)
            {
                cache.Set("ShopBrowse", new List<string>());
            }
            List<string> ShopBrowse = cache.Get<List<string>>("ShopBrowse");

            if (!ShopBrowse.Contains(id)) // 判断缓存中是否拥有该浏览记录
            {
                ShopBrowse.Add(id);
                cache.Remove("ShopBrowse");
                // 写入缓存
                cache.Set("ShopBrowse", ShopBrowse);
                return "浏览成功";
            }
            else
                return "已存在该浏览记录";





        }

        /// <summary>
        /// 显示浏览记录
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public List<string> ShowBrowse()
        {
            List<string> Browse = cache.Get<List<string>>("ShopBrowse");

            return Browse;
        }

        /// <summary>
        /// 输入 查询条件 输出 商品列表
        /// </summary>
        //编写接口获取商品列表数据，并且使用本地缓存保存查询结果，缓存时间为30分钟
        //如果输入参数 为请求类 类对象 不能使用get请求
        [HttpPost]
        public List<Good> GetGoods(GetGoodsRequest request)
        {

            //并且使用本地缓存保存查询结果
            //判斷有无缓存 如果有 直接返回缓存结果
            //                       key        如果有缓存存在  则在这里返回 data就是缓存返回的结果
            //第一次 搜索没有关键字 缓存的是所有的商品
            //第二次 搜索 小米 获取的缓存的结果（所有商品）
            //第三次 搜索 华为 获取的缓存的结果(所有商品)
            //想要的是 查询条件  跟查询结果 匹配的 
            //查询条件 跟缓存 关联
            var key = "GoodCache_" + request.Keyword + "_" + request.CateId;
            if (cache.TryGetValue(key, out object data))
            {
                //as转换类型
                return data as List<Good>;
            }
            //如果没有 才去查询数据库 数据保存到缓存 返回结果
            else
            {
                //获取商品列表数据  待执行SQL语句 根据查询条件拼接
                var good = _db.Goods.AsQueryable();

                if (!string.IsNullOrEmpty(request.Keyword))
                {
                    //如果关键字的条件不为空 加上查询条件
                    good = good.Where(x => x.Name.Contains(request.Keyword));
                }

                if (request.CateId > 0)
                {
                    //如果分类Id>0 加上查询条件
                    good = good.Where(x => x.CateId == request.CateId);
                }
                //返回商品列表数据
                var goodList = good.ToList();

                //数据保存到缓存                          30分钟
                //GoodCache+条件 保存的结果 就是该条件 专属的结果
                //"GoodCache__0" 所有商品 无查询条件
                //GoodCache_小米_0 小米关键字的商品
                cache.Set(key, goodList, TimeSpan.FromMinutes(30));
                return goodList;
            }
        }
        //需求分析
        //编写商品详情接口，用户浏览商品详情时使用本地缓存保存登录用户浏览商品记录
        //1、编写商品详情接口
        //2、使用本地缓存保存登录用户浏览商品记录
        /// <summary>
        /// 输入 主键：商品Id 输出 商品的详情信息（老师）
        /// </summary>
        [HttpGet]
        //加上认证标签 不加拿不到登录信息
        [Authorize]
        public Good GetGoodInfo(int goodId)
        {
            //查询逻辑 后端
            var good = _db.Goods.FirstOrDefault(x => x.Id == goodId);

            //使用本地缓存保存 登录用户浏览商品记录
            //userId 登录用户Id
            var userId = Response.HttpContext.User.Claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier).Value;
            //每个人是一样key? 不一样的，每个人的浏览记录都是单独的。
            var key = "GoodViewsCache_" + userId;

            //data 数据 用户的 浏览商品记录（用户看过的商品列表）
            //商品列表 组成 （以前看过的商品列表 + 这次商品）
            //获取以前的浏览商品列表
            var beforeData = cache.Get<List<Good>>(key);
            //如果没有以前看过的商品
            if (beforeData == null)
                beforeData = new List<Good>();
            //如果beforeData==null 对象为null 无法.方法 .属性



            if (beforeData.Count(x => x.Id == goodId) <= 0) // 判断缓存中是否拥有该浏览记录
            { // 不存在
                // beforeData.Add(good); // 不用添加 直接插入
                beforeData.Insert(0, good);
                var data = beforeData;
                cache.Set(key, data);
            }
            else
            { // 存在
                // 为了实现最新的浏览记录得先删除原来的该商品浏览记录，并插入到最开始表示最新浏览记录
                // 先删除
                beforeData.RemoveAll(x => x.Id == goodId);
                // 后插入
                beforeData.Insert(0, good);
            }



            //beforeData.Add(good);
            ////var data = beforeData;
            //cache.Set(key, data);
            return good;
        }

        /// <summary>
        /// 浏览记录（老师）
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        [Authorize]
        public List<Good> LLJL()
        {
            //userId 登录用户Id
            var userId = Response.HttpContext.User.Claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier).Value;
            var key = "GoodViewsCache_" + userId;
            //获取以前的浏览商品列表
            var beforeData = cache.Get<List<Good>>(key);
            return beforeData;
        }
    }
}
